package cn.darkhorse.mesadmin.task;

import cn.darkhorse.common.config.DbTypeConfig;
import cn.darkhorse.device.iot.constant.IotConstant;
import com.baomidou.mybatisplus.extension.toolkit.SqlRunner;
import com.ruoyi.common.utils.StringUtils;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 第三方系统数据处理定时任务
 * @author yuanjun
 * @since 20231108
 */

@Component("thirdSystemTask")
public class IotThirdSystemTask {

    public static final String CODE = "code";
    public static final String DAY_YIELD = "dayyield";
    // 需要处理的设备id，用英文逗号隔开
    private List<DeviceBO> deviceBOList;

    @Resource
    private DbTypeConfig dbTypeConfig;

    @Autowired
    public IotThirdSystemTask() {
        this.deviceBOList = new ArrayList<>();

        // 添加产量系统统计
        DeviceBO bo = new DeviceBO();
        bo.setGatewayDeviceId("scanner");
        bo.setPrimaryKeys(CODE);
        this.deviceBOList.add(bo);
    }


    /**
     * 筛选每日产量
     * 备注： 每日的产量信息会多次上传，因此每日只需要取最后一条有效数据即可
     * 设计思路： 从 history_scanner表中查询，写入history_day_scanner
     */
//    @Scheduled(cron = "0/10 * * * * *")
    public void filterDailyProductionData() {
        deviceBOList.forEach(bo-> {
            doFilter(bo);
        });
    }

    private void doFilter(DeviceBO bo) {
        String historyTableName = bo.getHistoryTableName();
        Ids ids = new Ids();

        // 每次取未处理过的前3000条数据进行处理，该插入插入 该更新更新
        String sql = dbTypeConfig.isMysql() ?
                String.format("select * from %s where flag = 0 order by id asc limit 5000", historyTableName) :
                String.format("select top 5000 * from %s where flag = 0 order by id asc", historyTableName);

        List<Map<String, Object>> sourceHistoryDataList = SqlRunner.db().selectList(sql);
        Map<String ,Map<String, Object>> targetHistoryDataMap = new HashMap<>();

        // 先把本次取出来的数据清洗一下
        sourceHistoryDataList.forEach(map-> {
            String key = map.get(CODE).toString();
            Map<String, Object> historyData = targetHistoryDataMap.get(key);
            if (historyData == null) {
                historyData = map;
                targetHistoryDataMap.put(key, historyData);
            }

            if (Long.parseLong(map.get(DAY_YIELD).toString()) > Long.parseLong(historyData.get(DAY_YIELD).toString())) {
                targetHistoryDataMap.put(key, map);
            }
            ids.add(map.get("id").toString());
        });

        // 查询 history_canner_day,以此来判断是该插入还是更新。
        Iterator<String> iterator = targetHistoryDataMap.keySet().iterator();
        while (iterator.hasNext()) {
            String code = iterator.next();
            Map<String, Object> data = targetHistoryDataMap.get(code);

            String querySql = String.format("select * from %s where code = '%s'"
                    , bo.getHistoryDayTableName(), code);
            List<Map<String, Object>> maps = SqlRunner.db().selectList(querySql);
            if (maps.size() > 0) {
                // 进行更新操作
                Map<String, Object> oldData = maps.get(0);
                // 如果新数据大于老数据，则进行更新操作
                if (Long.parseLong(data.get(DAY_YIELD).toString()) > Long.parseLong(oldData.get(DAY_YIELD).toString())) {
                    String updateSql = buildUpdateSql(data, bo);
                    SqlRunner.db().update(updateSql);
                }
            }else {
                // 进行插入操作
                String insertSql = buildInsertSql(data, bo);
                SqlRunner.db().insert(insertSql);
            }
        }


        // 更新history_data表flag
        if (ids.getLastId() == null || ids.getFirstId() == null) {
            return;
        }

        String updateFlagSql = String.format("update %s set flag = 1 where id < %s and id > %s ", bo.getHistoryTableName()
                , ids.getLastId() + 1, ids.getFirstId() - 1);
        SqlRunner.db().update(updateFlagSql);
    }

    /**
     * 构造更新日数据的sql
     * @param historyData
     * @param bo
     * @return
     */
    private String buildUpdateSql(Map<String, Object> historyData, DeviceBO bo) {
        Set<String> fields = historyData.keySet();

        String update = String.format("UPDATE %s", bo.getHistoryDayTableName());
        String set = " set %s ";
        String where = String.format(" where code = '%s'", historyData.get(CODE).toString());

        StringBuilder setSb = new StringBuilder();

        fields.forEach(fieldName-> {
            if ("id".equals(fieldName) || "flag".equals(fieldName) || "time".equals(fieldName)) {
                return;
            }
            setSb.append(dbTypeConfig.getFrontBracket()).append(fieldName).append(dbTypeConfig.getBackBracket())
                    .append("=").
                    append("'").append(historyData.get(fieldName).toString()).append("'").append(",");
        });

        set = String.format(set, setSb.subSequence(0, setSb.length() - 1));

        return update + set + where;
    }

    /**
     * 构造插入日数据的sql
     * @param historyData
     * @param bo
     * @return
     */
    private String buildInsertSql(Map<String, Object> historyData, DeviceBO bo) {
        Set<String> fields = historyData.keySet();
        String insert = String.format("insert into %s",bo.getHistoryDayTableName());
        String field = " (%s) ";
        String values = " values(%s) ";

        StringBuilder fieldSb = new StringBuilder();
        StringBuilder valuesSb = new StringBuilder();

        fields.forEach(fieldName-> {
            if ("id".equals(fieldName) || "flag".equals(fieldName)) {
                return;
            }
            fieldSb.append(dbTypeConfig.getFrontBracket()).append(fieldName).append(dbTypeConfig.getBackBracket()).append(",");
            valuesSb.append("'").append(historyData.get(fieldName).toString()).append("'").append(",");
        });

        // 计算班次
        fieldSb.append(dbTypeConfig.getFrontBracket()).append("classes").append(dbTypeConfig.getBackBracket()).append(",");
        Classes classes = calculateShiftType(historyData.get("time").toString());
        valuesSb.append(classes.getClasses()).append(",");

        fieldSb.append(dbTypeConfig.getFrontBracket()).append("classes_date").append(dbTypeConfig.getBackBracket()).append(",");
        valuesSb.append(classes.getClassesDate()).append(",");

        field = String.format(field, fieldSb.subSequence(0, fieldSb.length() - 1));
        values = String.format(values, valuesSb.subSequence(0, valuesSb.length() - 1));

        return insert + field + values;
    }

    /**
     * 根据所传时间分析是夜班还是白班
     * @param dateTime
     * @return
     */
    private Classes calculateShiftType(String dateTime) {
        String[] split = {"000000","090000","210000","240000"};

        String date = dateTime.substring(0,8);
        String time = dateTime.substring(8, dateTime.length());

        Classes classes = new Classes();
        classes.setClassesDate(date);

        if (time.compareTo(split[0])>=0 && time.compareTo(split[1]) < 0) {
            DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");
            LocalDate day = LocalDate.parse(date, yyyyMMdd);
            LocalDate subtractedDate = day.minusDays(1);
            classes.setClasses(1);
            classes.setClassesDate(subtractedDate.format(yyyyMMdd));
        }
        if (time.compareTo(split[1])>=0 && time.compareTo(split[2]) < 0) {
            classes.setClasses(0);
        }

        if (time.compareTo(split[2])>=0 && time.compareTo(split[3]) < 0) {
            classes.setClasses(1);
        }

        return classes;
    }

    @Data
    private class Classes{
        /**
         * 班次
         */
        private Integer classes = -1;
        /**
         * 班次日期
         */
        private String classesDate;
    }


    @Data
    private class Ids {
        Long lastId;
        Long firstId;

        public void add(String strId) {
            if (StringUtils.isEmpty(strId)) {
                return;
            }

            Long id = Long.parseLong(strId);

            // 判断条件可以合并，但是分开写更加清晰
            if (this.lastId == null) {
                lastId = id;
            }

            if (this.firstId == null) {
                firstId = id;
            }

            if (lastId < id) {
                this.lastId = id;
            }

            if (firstId > id) {
                this.firstId =id;
            }
        }
    }


    @Data
    private class DeviceBO {
        private String gatewayDeviceId;
        private String primaryKeys = "id";
        private Long lastId;

        public String getHistoryTableName() {
            return IotConstant.HISTORY_TABLE_NAME_PRE + gatewayDeviceId;
        }

        public String getHistoryDayTableName() {
            return IotConstant.HISTORY_TABLE_NAME_PRE + gatewayDeviceId + IotConstant.HISTORY_DAY_TABLE_NAME_DAY;
        }
    }
}
